package org.extremengine;

import java.util.Calendar;

/**
 * Timer for generating frames.
 * You can pauseTimer(), resumeTimer() and stopTimer().
 * The differences with JDK's Timer is that if you pauseTimer(), it also pause the time counter.
 * @author yuxing
 * 
 */
public class TimerThread extends Thread {
	public static final int STATUS_RUNNING = 1;
	public static final int STATUS_PAUSED = 2;
	public static final int STATUS_STOPED = 3;

	private TimerTask task;

	// Duration between each frame, millisecond.
	private int duration;

	// Current time in millisecond.
	private long time = 0;

	// Timer status, set it to be STATUS_STOPED after starting will cause thread abort.
	private int status = STATUS_STOPED;
	
	// Automatically resume from pause if pauseTimeSpan is not 0.
	private long pauseTimeSpan = 0;

	public TimerThread() {
		super("extremengine");
	}

	
	@Override
	public void run() {
		status = STATUS_RUNNING;
		
		// Start time and end time for each round, to determing the FPS.
		long startTime;
		long endTime;
		for (;;) {
			
			if(status == STATUS_PAUSED) {
				synchronized (task) {
					try {
						if(pauseTimeSpan == 0) {
							task.wait();
						}
						else {
							//好像不工作
							task.wait(pauseTimeSpan);
						}
					} catch (InterruptedException e) {
						e.printStackTrace();
					}	
				}
			}
			if(status == STATUS_STOPED) {
				break;
			}
			
			startTime = Calendar.getInstance().getTimeInMillis();

			task.run(time);

			endTime = Calendar.getInstance().getTimeInMillis();

			// How long the task took.
			long usedTime = endTime - startTime;

			// How many full time pieces it took.
			int count = (int) usedTime / duration;

			// Partial time of piece it took.
			long exceedTime = usedTime % duration;

			// It just on time, not wait()
			if (count > 0 && exceedTime == 0) {
				time += count * duration;
			}
			// Need wait to next time piece.
			else {
				synchronized (task) {
					try {
						// Task took no time(this condition is impossible but required).
						if(count == 0 && exceedTime == 0) {
							task.wait(duration);
							time += duration;
						}
						else {
							task.wait(duration - exceedTime);
							time += (count + 1) * duration;
						}
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
			}
		}
	}

	/**
	 * Run task at a fixed rate, if task slow, it will skip to next time piece instead of catching up,
	 * 
	 * @param task
	 * @param duration millisecond
	 */
	public void runAtFixedRate(TimerTask task, int duration) {
		this.task = task;
		this.duration = duration;
		this.start();
	}

	/**
	 * Pause timer if running, time will pause also.
	 */
	public void pauseTimer() {
		if (task != null && status == STATUS_RUNNING) {
			status = STATUS_PAUSED;
		}
	}

	public void pauseTimer(long timeout) {
		this.pauseTimeSpan = timeout;
		pauseTimer();
	}
	
	/**
	 * Resume timer from Pause.
	 */
	public void resumeTimer() {
		if (task != null && status == STATUS_PAUSED) {
			status = STATUS_RUNNING;
			synchronized (task) {
				task.notifyAll();
			}
		}
	}
	
	/**
	 * Set timer status flag to be STATUS_STOPED to stop timer later.
	 */
	public void stopTimer() {
		if(task != null ) {
			status = STATUS_STOPED;
			synchronized (task) {
				task.notifyAll();
			}
		}
	}

	/**
	 * 
	 * @return
	 */
	public int getStatus() {
		return status;
	}
}
